home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / AppsToGo / Kibitz / Chess.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-22  |  58.8 KB  |  2,161 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        chess.c
  5. ** Written by:  Eric Soldan
  6. **
  7. ** Copyright © 1990-1992 Apple Computer, Inc.
  8. ** All rights reserved. */
  9.  
  10.  
  11.  
  12. /*****************************************************************************/
  13.  
  14.  
  15.  
  16. #include "Kibitz.h"                /* Get the Kibitz includes/typedefs, etc.    */
  17. #include "KibitzCommon.h"        /* Get the stuff in common with rez.        */
  18. #include "Kibitz.protos"        /* Get the prototypes for Kibitz.            */
  19.  
  20. #ifndef __ERRORS__
  21. #include <Errors.h>
  22. #endif
  23.  
  24. #ifndef __FONTS__
  25. #include <Fonts.h>
  26. #endif
  27.  
  28. #ifndef __TEXTEDITCONTROL__
  29. #include <TextEditControl.h>
  30. #endif
  31.  
  32. #ifndef __TOOLUTILS__
  33. #include <ToolUtils.h>
  34. #endif
  35.  
  36. #ifndef THINK_C
  37. #ifndef __STRINGS__
  38. #include <Strings.h>
  39. #endif
  40. #endif
  41.  
  42. #ifndef __UTILITIES__
  43. #include <Utilities.h>
  44. #endif
  45.  
  46.  
  47.  
  48. /*****************************************************************************/
  49.  
  50.  
  51.  
  52. extern Boolean    gComputerResigns;
  53.  
  54. short    gPieceLoc;
  55.  
  56. #define kLastNode         6
  57. #define kComputerResigns -9999
  58.  
  59. GameListHndl            gGenMovesHndl;
  60.  
  61. static unsigned long    idleTick;
  62. static MoveListHndl        gNodeHndl[kLastNode + 1];
  63.  
  64. static short    gNumPieces, gPosReps;
  65. static long    gTreeValue, gWhiteTotal, gBlackTotal;
  66. static long    gTreePieceValues[13] = {
  67.     -0x40000000L, -0x00090000L, -0x00050000L, -0x00030000L, -0x00030000L, -0x00010000L,
  68.      0x00000000L,
  69.      0x00010000L,  0x00030000L,  0x00030000L,  0x00050000L,  0x00090000L,  0x40000000L,
  70. };
  71.  
  72. static short    distance[10] = {0, 1, 1, 7, 7, 7, 1};
  73.     /* How far a piece can move.                        */
  74.     /* The double-pawn-push is handled as an exception. */
  75.     /* Castling is handled as an exception.                */
  76.  
  77. static short    direction[10][9] = {
  78.       0,   0,   0,   0,   0,   0,   0,   0,   0,
  79.      10,   9,  11,   0,   0,   0,   0,   0,   0,    /* Pawn moves.     */
  80.     -21, -19, -12,  -8,   8,  12,  19,  21,   0,    /* Knight moves. */
  81.     -11,  -9,   9,  11,   0,   0,   0,   0,   0,    /* Bishop moves. */
  82.     -10,  -1,   1,  10,   0,   0,   0,   0,   0,    /* Rook moves.     */
  83.     -11,  -9,   9,  11, -10,  -1,   1,  10,   0,    /* Queen moves.     */
  84.     -11,  -9,   9,  11, -10,  -1,   1,  10,   0,    /* King moves.     */
  85. };
  86.  
  87. static short    gPieceColor[13] = {WHITE, WHITE, WHITE, WHITE, WHITE, WHITE,
  88.                                    EMPTY,
  89.                                    BLACK, BLACK, BLACK, BLACK, BLACK, BLACK};
  90.  
  91. static short    gPieceKind[13]  = {KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN,
  92.                                    EMPTY,
  93.                                    PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING};
  94.  
  95.  
  96.  
  97. /*****************************************************************************/
  98.  
  99.  
  100.  
  101. TheDoc    newDocData
  102. #ifndef __PPCC__
  103.     /*
  104.         Work around compiler bug with aggregate initializers of
  105.         mixed alignment structures (68k structs containing PowerPC
  106.         structs).
  107.     */
  108.  
  109.  = {
  110.     kVersion,        /* File format version.                             */
  111.  
  112.     false,            /* Flag indicating print record is current.         */
  113.     {                /* Space for print record.                         */
  114.         0,
  115.         {0, 0, 0,{0, 0, 0, 0},},
  116.         {0, 0, 0, 0},
  117.         {0, 0, 0, 0, 0},
  118.         {0, 0, 0,{0, 0, 0, 0},},
  119.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  120.         {0, 0, 0, 0, 0, nil, nil, 0, 0, 0},
  121.         {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  122.     },
  123.  
  124.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  125.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  126.     OBNDS,  BR,    BN,    BB,    BQ,    BK,    BKB,   BKN,   BKR,  OBNDS,
  127.     OBNDS,  BP,    BP,    BP,    BP,    BP,    BP,    BP,    BP,   OBNDS,
  128.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  129.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  130.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  131.     OBNDS,   0,     0,     0,     0,     0,     0,     0,     0,   OBNDS,
  132.     OBNDS,  WP,    WP,    WP,    WP,    WP,    WP,    WP,    WP,   OBNDS,
  133.     OBNDS,  WR,    WN,    WB,    WQ,    WK,    WKB,   WKN,   WKR,  OBNDS,
  134.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  135.     OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS, OBNDS,
  136.     {
  137.         WKPOS, 0,    /* White king position, king-moved count.         */
  138.         0,            /* White queen-rook-moved count.                 */
  139.         0,            /* White king-rook-moved count.                     */
  140.         BKPOS, 0,    /* Black king position, king-moved count.         */
  141.         0,            /* Black queen-rook-moved count.                 */
  142.         0            /* Black king-rook-moved count.                     */
  143.     },
  144.     0,                /* En-passant opportunity location.                 */
  145.     0,                /* En-passant opportunity pawn-to-take location. */
  146.     0,                /* Arranged en-passant opportunity loc.             */
  147.     0,                /* Arranged en-passant opp. pawn-to-take loc.     */
  148.     0,                /* Number of legal moves in move list.             */
  149.     0,                /* Index into record of game.                     */
  150.     0,                /* Number of moves in game.                         */
  151.     0,                /* My color (0 = white).                         */
  152.     0,                /* True if black started game.                     */
  153.     0,                /* True if in arrange-board mode.                 */
  154.     WP,                /* Piece hilited in arrange-board palette.         */
  155.     18000L,            /* 5 minute default time for white.                 */
  156.     18000L,            /* 5 minute default time for black.                 */
  157.     -1L,            /* Ticks remaining for white. (-1, no clock)     */
  158.     -1L,            /* Ticks remaining for black. (-1, no clock)     */
  159.     false,            /* Display board normal (not inverted.)             */
  160.     0,                /* Above info is saved to disk.                     */
  161.  
  162.     "\0",            /* Space for opponent zone.                         */
  163.     "\0",            /* Space for opponent machine.                     */
  164.     "\0",            /* Space for opponent kibitz full path.             */
  165.     "\0",            /* Space for opponent kibitz filename.             */
  166.     false,            /* Boolean for just board window.                 */
  167.     false,            /* Boolean for document is template.             */
  168.     false,            /* Document saved while computer moves white.     */
  169.     false,            /* Document saved while computer moves black.     */
  170.     0,                /* Above is new version info saved to disk.         */
  171.  
  172.     false,            /* Flag indicating existence of opponent.         */
  173.     0L,                /* ID assigned by me for this game.                 */
  174.     0L,                /* ID assigned by opponent for this game.         */
  175.     0,                /* Reason for sending the game.                     */
  176.     0,                /* State of the draw button.                      */
  177.     {
  178.         0L,            /* AEAddressDesc of opponent.                     */
  179.         nil
  180.     },
  181.     0,                /* Above is send game info.                         */
  182.  
  183.     0,                /* State for receiving AppleEvents.                 */
  184.     false,            /* Flag indicating if we originated game.         */
  185.     -1L,            /* Ticks remaining displayed for white.             */
  186.     -1L,            /* Ticks remaining displayed for black.             */
  187.     -1L,            /* Ticks for freeze clock for white.             */
  188.     -1L,            /* Ticks for freeze clock for black.             */
  189.     0L,                /* Reference tick for timer.                     */
  190.     0L,                /* Tick when computer moved last.                 */
  191.     0L,                /* Tick when received last info from opponent.     */
  192.     false,            /* Flag indicating computer moves white pieces.     */
  193.     false,            /* Flag indicating computer moves black pieces.     */
  194.     "\0",            /* Space for opponent name.                         */
  195.     "\0",            /* Space for opponent zone.                         */
  196.     "\0",            /* Space for opponent machine.                     */
  197.     0,                /* Time that last move/message received.         */
  198.     0,                /* Above info is for one machine only.             */
  199.  
  200.     false,            /* Flag indicating color change has been posted. */
  201.     0,                /* New color from config.                         */
  202.     false,            /* Flag indicating time change has been posted.     */
  203.     -1L,            /* New white time from config.                     */
  204.     -1L,            /* New black time from config.                     */
  205.     0,                /* Above info is config setting which will         */
  206.                     /* not be applied until a NULL event.             */
  207.  
  208.     nil,            /* Handle to legal move list.                     */
  209.     nil,            /* Handle to game record.                         */
  210.     nil,            /* Handle to incoming message.                     */
  211.     nil,            /* Handle to outgoing message.                     */
  212.     nil,            /* Handle to recorded sound, if any.             */
  213.     false,            /* Boolean stating if we say stuff.                 */
  214.     {
  215.         0L,            /* Default voice.                                 */
  216.         0L
  217.     },
  218.     nil,            /* Handle to send button control.                 */
  219.     nil,            /* Handle to move notify control.                 */
  220.     nil,            /* Handle to message notify control.             */
  221.     nil,            /* Handle to game-slider control.                 */
  222.     nil,            /* Handle to white-starts radio button.             */
  223.     nil,            /* Handle to black-starts radio button.             */
  224.     nil,            /* Handle to resign button.                         */
  225.     nil,            /* Handle to draw button.                         */
  226.     nil,            /* Handle to record sound button.                 */
  227.     nil,            /* Handle to send sound button.                     */
  228.     0,                /* Above info is reference to controls.             */
  229. }
  230. #endif /* __PPCC__ */
  231. ;
  232.  
  233. /*****************************************************************************/
  234.  
  235. #ifdef __PPCC__
  236.     /*
  237.         Work around compiler bug with aggregate initializers of
  238.         mixed alignment structures (68k structs containing PowerPC
  239.         structs).
  240.     */
  241. void InitNewDocData(void)
  242. {
  243.     int i,j;
  244.     
  245.     newDocData.version = kVersion;
  246.     
  247.     newDocData.printRecValid = false;
  248.     newDocData.print.iPrVersion = 0;
  249.     newDocData.print.prInfo.iDev = 0;
  250.     newDocData.print.prInfo.iVRes = 0;
  251.     newDocData.print.prInfo.iHRes = 0;
  252.     newDocData.print.prInfo.rPage.top = 0;
  253.     newDocData.print.prInfo.rPage.left = 0;
  254.     newDocData.print.prInfo.rPage.bottom = 0;
  255.     newDocData.print.prInfo.rPage.right = 0;
  256.     newDocData.print.rPaper.top = 0;
  257.     newDocData.print.rPaper.left = 0;
  258.     newDocData.print.rPaper.bottom = 0;
  259.     newDocData.print.rPaper.right = 0;
  260.     newDocData.print.prStl.wDev = 0;
  261.     newDocData.print.prStl.iPageV = 0;
  262.     newDocData.print.prStl.iPageH = 0;
  263.     newDocData.print.prStl.bPort = 0;
  264.     newDocData.print.prStl.feed = 0;
  265.     newDocData.print.prInfoPT.iDev = 0;
  266.     newDocData.print.prInfoPT.iVRes = 0;
  267.     newDocData.print.prInfoPT.iHRes = 0;
  268.     newDocData.print.prInfoPT.rPage.top = 0;
  269.     newDocData.print.prInfoPT.rPage.left = 0;
  270.     newDocData.print.prInfoPT.rPage.bottom = 0;
  271.     newDocData.print.prInfoPT.rPage.right = 0;
  272.     newDocData.print.prXInfo.iRowBytes = 0;
  273.     newDocData.print.prXInfo.iBandV = 0;
  274.     newDocData.print.prXInfo.iBandH = 0;
  275.     newDocData.print.prXInfo.iDevBytes = 0;
  276.     newDocData.print.prXInfo.iBands = 0;
  277.     newDocData.print.prXInfo.bPatScale = 0;
  278.     newDocData.print.prXInfo.bUlThick = 0;
  279.     newDocData.print.prXInfo.bUlOffset = 0;
  280.     newDocData.print.prXInfo.bUlShadow = 0;
  281.     newDocData.print.prXInfo.scan = 0;
  282.     newDocData.print.prXInfo.bXInfoX = 0;
  283.     newDocData.print.prJob.iFstPage = 0;
  284.     newDocData.print.prJob.iLstPage = 0;
  285.     newDocData.print.prJob.iCopies = 0;
  286.     newDocData.print.prJob.bJDocLoop = 0;
  287.     newDocData.print.prJob.fFromUsr = 0;
  288.     newDocData.print.prJob.pIdleProc = nil;
  289.     newDocData.print.prJob.pFileName = nil;
  290.     newDocData.print.prJob.iFileVol = 0;
  291.     newDocData.print.prJob.bFileVers = 0;
  292.     newDocData.print.prJob.bJobX = 0;
  293.     for (i=0; i < 19; ++i)
  294.         newDocData.print.printX[i] = 0;
  295.  
  296.     for ( i = 0; i < 120; ++i )
  297.        newDocData.theBoard[i] = OBNDS;
  298.     newDocData.theBoard[21] = BR;
  299.     newDocData.theBoard[22] = BN;
  300.     newDocData.theBoard[23] = BB;
  301.     newDocData.theBoard[24] = BQ;
  302.     newDocData.theBoard[25] = BK;
  303.     newDocData.theBoard[26] = BKB;
  304.     newDocData.theBoard[27] = BKN;
  305.     newDocData.theBoard[28] = BKR;
  306.     newDocData.theBoard[31] = BP;
  307.     newDocData.theBoard[32] = BP;
  308.     newDocData.theBoard[33] = BP;
  309.     newDocData.theBoard[34] = BP;
  310.     newDocData.theBoard[35] = BP;
  311.     newDocData.theBoard[36] = BP;
  312.     newDocData.theBoard[37] = BP;
  313.     newDocData.theBoard[38] = BP;
  314.     for ( i = 40; i <= 70; i += 10 )
  315.        for (j = 1; j<= 8; ++j)
  316.           newDocData.theBoard[i+j] = 0;
  317.     newDocData.theBoard[81] = WP;
  318.     newDocData.theBoard[82] = WP;
  319.     newDocData.theBoard[83] = WP;
  320.     newDocData.theBoard[84] = WP;
  321.     newDocData.theBoard[85] = WP;
  322.     newDocData.theBoard[86] = WP;
  323.     newDocData.theBoard[87] = WP;
  324.     newDocData.theBoard[88] = WP;
  325.     newDocData.theBoard[91] = WR;
  326.     newDocData.theBoard[92] = WN;
  327.     newDocData.theBoard[93] = WB;
  328.     newDocData.theBoard[94] = WQ;
  329.     newDocData.theBoard[95] = WK;
  330.     newDocData.theBoard[96] = WKB;
  331.     newDocData.theBoard[97] = WKN;
  332.     newDocData.theBoard[98] = WKR;
  333.  
  334.     newDocData.king[0].kingLoc = WKPOS;
  335.     newDocData.king[0].kingMoves = 0;
  336.     newDocData.king[0].rookMoves[0] = 0;
  337.     newDocData.king[0].rookMoves[1] = 0;
  338.     newDocData.king[1].kingLoc = BKPOS;
  339.     newDocData.king[1].kingMoves = 0;
  340.     newDocData.king[1].rookMoves[0] = 0;
  341.     newDocData.king[1].rookMoves[1] = 0;
  342.     
  343.     newDocData.enPasMove = 0;
  344.     newDocData.enPasPawnLoc = 0;
  345.     newDocData.arngEnPasMove = 0;
  346.     newDocData.arngEnPasPawnLoc = 0;
  347.     newDocData.numLegalMoves = 0;        
  348.     newDocData.gameIndex = 0;        
  349.     newDocData.numGameMoves = 0;            
  350.     newDocData.myColor = 0;            
  351.     newDocData.startColor = 0;                
  352.     newDocData.arrangeBoard = 0;                
  353.     newDocData.palettePiece = WP;            
  354.     newDocData.defaultTime[0] = 18000L;    
  355.     newDocData.defaultTime[1] = 18000L;        
  356.     newDocData.timeLeft[0] = -1L;        
  357.     newDocData.timeLeft[1] = -1L;            
  358.     newDocData.invertBoard = false;        
  359.     newDocData.endFileInfo1 = 0;
  360.     
  361.     ccpy(newDocData.reconnectZone,"\0");            
  362.     ccpy(newDocData.reconnectMachine,"\0");            
  363.     ccpy(newDocData.reconnectPath,"\0");    
  364.     ccpy(newDocData.reconnectApp,"\0");
  365.     
  366.     newDocData.justBoardWindow = false;
  367.     newDocData.docIsTemplate = false;
  368.     newDocData.keepCMWhite = false;
  369.     newDocData.keepCMBlack = false;
  370.     newDocData.endFileInfo2 = 0;                
  371.  
  372.     newDocData.twoPlayer = false;            
  373.     newDocData.gameID_0 = 0L;                
  374.     newDocData.gameID_1 = 0L;                
  375.     newDocData.sendReason = 0;                
  376.     newDocData.drawBtnState = 0;    
  377.     
  378.     newDocData.locOfOpponent.descriptorType = 0L;
  379.     newDocData.locOfOpponent.dataHandle = nil;
  380.     newDocData.endSendInfo = 0;
  381.     
  382.     newDocData.resync = 0;
  383.     newDocData.creator = false;
  384.     newDocData.displayTime[0] = -1L;            
  385.     newDocData.displayTime[1] = -1L;            
  386.     newDocData.freezeTime[0] = -1L;            
  387.     newDocData.freezeTime[1] = -1L;            
  388.     newDocData.timerRefTick = 0L;
  389.     newDocData.compMoveTick = 0L;
  390.     newDocData.gotUpdateTick = 0L;
  391.     newDocData.compMovesWhite = false;
  392.     newDocData.compMovesBlack = false;
  393.     
  394.     ccpy(newDocData.opponentName,"\0");            
  395.     ccpy(newDocData.opponentZone,"\0");            
  396.     ccpy(newDocData.opponentMachine,"\0");
  397.     
  398.     newDocData.timeLastReceive = 0;
  399.     newDocData.endLocalInfo = 0;
  400.     
  401.     newDocData.configColorChange = false;
  402.     newDocData.configColor = 0;
  403.     newDocData.configTimeChange = false;
  404.     newDocData.configTime[0] = -1L;
  405.     newDocData.configTime[1] = -1L;
  406.     newDocData.endConfigInfo = 0;
  407.     
  408.     newDocData.legalMoves = nil;
  409.     newDocData.gameMoves = nil;
  410.     newDocData.message[0] = nil;
  411.     newDocData.message[1] = nil;
  412.     newDocData.sound = nil;
  413.     newDocData.doSpeech = false;
  414.     newDocData.theVoice.creator = 0L;
  415.     newDocData.theVoice.id = 0L;
  416.     
  417.     newDocData.sendMessage = nil;
  418.     newDocData.beepOnMove = nil;
  419.     newDocData.beepOnMssg = nil;
  420.     newDocData.gameSlider = nil;
  421.     newDocData.wbStart[0] = nil;
  422.     newDocData.wbStart[1] = nil;
  423.     newDocData.resign = nil;
  424.     newDocData.draw = nil;
  425.     newDocData.record = nil;
  426.     newDocData.sendSnd = nil;
  427.     newDocData.endControls = 0;
  428. }
  429. #endif /* __PPCC__ */
  430.  
  431.  
  432. /*****************************************************************************/
  433. /*****************************************************************************/
  434.  
  435. #ifdef applec
  436. #pragma segment Chess
  437. #endif
  438.  
  439. /*****************************************************************************/
  440. /*****************************************************************************/
  441.  
  442.  
  443.  
  444. OSErr    InitLogic(void)
  445. {
  446.     short    i;
  447.  
  448.     if (!(gGenMovesHndl = (GameListHndl)NewHandle(0))) return(memFullErr);
  449.  
  450.     for (i = 0; i <= kLastNode; ++i)
  451.         if (!(gNodeHndl[i] = (MoveListHndl)NewHandle(0))) return(memFullErr);
  452.  
  453.     return(noErr);
  454. }
  455.  
  456.  
  457.  
  458. /*****************************************************************************/
  459.  
  460.  
  461.  
  462. void    NewGame(FileRecHndl game)
  463. {
  464.     TheDocPtr    docPtr;
  465.  
  466.     docPtr = &(*game)->doc;
  467.  
  468.     newDocData.legalMoves = docPtr->legalMoves;
  469.     newDocData.gameMoves  = docPtr->gameMoves;
  470.  
  471.     *docPtr = newDocData;
  472.  
  473.     newDocData.legalMoves = nil;
  474.     newDocData.gameMoves  = nil;
  475. }
  476.  
  477.  
  478.  
  479. /*****************************************************************************/
  480.  
  481.  
  482.  
  483. void    GenerateLegalMoves(FileRecHndl game)
  484. {
  485.     short            gameIndex, numGameMoves, square, piece;
  486.     short            color, pieceColor;
  487.     short            row, dirNum, dir, dist;
  488.     short            s, d, dest, destColor, epLoc;
  489.     long            size;
  490.     Boolean            docDirty, check, kspiece, dkspiece;
  491.     GameListHndl    gameMoves;
  492.  
  493.     gameIndex    = (*game)->doc.gameIndex;
  494.     gameMoves    = (*game)->doc.gameMoves;
  495.     numGameMoves = (*game)->doc.numGameMoves;
  496.     docDirty     = (*game)->fileState.docDirty;
  497.  
  498.     (*game)->doc.numLegalMoves = 0;            /* Start the list over. */
  499.  
  500.     if ((gameIndex) && (gameIndex == numGameMoves))
  501.         if (!(**gameMoves)[gameIndex - 1].moveFrom) return;
  502.             /* Resignation or agreed-upon draw recorded.
  503.             ** Game over, so no legal moves. */
  504.  
  505.     SetHandleSize((Handle)gGenMovesHndl, size = GetHandleSize((Handle)gameMoves));
  506.     BlockMove(*(Handle)gameMoves, *(Handle)gGenMovesHndl, size);
  507.     (*game)->doc.gameMoves = gGenMovesHndl;            /* Protect the game moves list. */
  508.  
  509.     color = WhosMove(game);                        /* Who's move it is. */
  510.  
  511.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  512.         /* Scan for pieces of correct color. */
  513.  
  514.         if ((piece = (*game)->doc.theBoard[square]) == EMPTY) continue;
  515.             /* Empty square, so next square, please. */
  516.  
  517.         if (piece == OBNDS) continue;
  518.             /* Out of bounds. */
  519.  
  520.         kspiece = false;
  521.         if (piece < WK) {
  522.             kspiece = true;
  523.             piece += KSIDEPIECE;
  524.         }
  525.         if (piece > BK) {
  526.             kspiece = true;
  527.             piece -= KSIDEPIECE;
  528.         }
  529.  
  530.         pieceColor = BLACK;
  531.         if (piece < 0) {
  532.             pieceColor = WHITE;
  533.             piece = -piece;
  534.         }
  535.  
  536.         if (pieceColor != color) continue;
  537.             /* Not our piece. */
  538.  
  539.         row = square / 10;
  540.  
  541.         for (dirNum = 0; (dir = direction[piece][dirNum]) != 0; ++dirNum) {
  542.             /* The direction we will move a piece.  This is correct in all
  543.             ** cases except for white pawns.  Without an adjustment, they
  544.             ** would move in the direction of black pawns, i.e., backwards.
  545.             */
  546.  
  547.             dist = distance[piece];
  548.                 /* The distance a piece can move, in all cases except a
  549.                 ** double-pawn-push and castling.
  550.                 */
  551.  
  552.             if (piece == PAWN) {
  553.                 if (color == WHITE) dir = -dir;
  554.                     /* White pawns will now move forwards. */
  555.  
  556.                 if ((!dirNum) && ((row == 3) || (row == 8))) dist = 2;
  557.                     /* In the case of a pawn, the first direction we check
  558.                     ** is forwards, so if dirNum is 0, we are pushing pawns.
  559.                     **
  560.                     ** Allow double-pawn-push if pawn is on correct row.  We don't
  561.                     ** have to worry about which color the pawn is if the pawn
  562.                     ** has advanced to the other double-push row.  It will
  563.                     ** double-push itself out of bounds.
  564.                     */
  565.             }
  566.  
  567.             for (s = square, d = 1; d <= dist; ++d) {
  568.  
  569.                 s += dir;
  570.                 dest = (*game)->doc.theBoard[s];
  571.                 if (dest == OBNDS) break;    /* Can't go this direction anymore. */
  572.  
  573.                 dkspiece = false;
  574.                 if (dest < WK) {
  575.                     dkspiece = true;
  576.                     dest += KSIDEPIECE;
  577.                 }
  578.                 if (dest > BK) {
  579.                     dkspiece = true;
  580.                     dest -= KSIDEPIECE;
  581.                 }
  582.  
  583.                 destColor = BLACK;
  584.                 if (dest < 0) {
  585.                     destColor = WHITE;
  586.                     dest = -dest;
  587.                 }
  588.  
  589.                 if ((dest) && (destColor == color)) break;
  590.                     /* Ran into our own piece, so can't go this direction anymore. */
  591.  
  592.                 if (dest == KING) break;
  593.                     /* Never allow the king to be taken. */
  594.  
  595.                 if (piece == PAWN) {
  596.                     if (!dirNum) {                /* If pawn push... */
  597.                         if (dest) break;        /* Can't take on a pawn-push. */
  598.                     }
  599.                     else {
  600.                         if (dest == EMPTY) {    /* If possible en-passant... */
  601.                             if ((*game)->doc.enPasMove != s) break;
  602.                                 /* Not en-passant pawn capture. */
  603.                             epLoc = (*game)->doc.enPasPawnLoc;
  604.                             dest  = (*game)->doc.theBoard[epLoc];
  605.                             destColor = BLACK;
  606.                             if (dest < 0) {
  607.                                 destColor = WHITE;
  608.                                 dest = -dest;
  609.                             }
  610.                             if (destColor == color) break;
  611.                                 /* We can't en-passant our own piece. */
  612.                             if (dest != PAWN) break;
  613.                                 /* We can only en-passant pawns. */
  614.                         }
  615.                     }
  616.                 }
  617.  
  618.                 MakeMove(game, square, s, QUEEN);
  619.                 check = SquareAttacked(game,
  620.                     (*game)->doc.king[color].kingLoc, color);
  621.                 UnmakeMove(game);
  622.                 if (!check) AddLegalMove(game, square, s);
  623.                     /* Move didn't put (or leave) king in check, so it is a
  624.                     ** valid move.  Since it is valid, record it. */
  625.  
  626.                 if (dest) break;    /* Once we hit a piece, we are
  627.                                     ** done in this direction. */
  628.             }
  629.         }
  630.     }
  631.  
  632.     square = (*game)->doc.king[color].kingLoc;
  633.     if (CastleOkay(game, QSIDE)) AddLegalMove(game, square, square - 2);
  634.     if (CastleOkay(game, KSIDE)) AddLegalMove(game, square, square + 2);
  635.         /* If castling possible, add it to move list. */
  636.  
  637.     (*game)->doc.gameMoves      = gameMoves;
  638.     (*game)->doc.numGameMoves   = numGameMoves;
  639.     (*game)->fileState.docDirty = docDirty;
  640.         /* Restore things the way we were.  We are done with MakeMove services. */
  641. }
  642.  
  643.  
  644.  
  645. /*****************************************************************************/
  646.  
  647.  
  648.  
  649. void    AddLegalMove(FileRecHndl game, short from, short to)
  650. {
  651.     MoveListHndl    lglMoves;
  652.     short            numLglMoves;
  653.     long            newHndlSize, oldHndlSize;
  654.  
  655.     numLglMoves = (*game)->doc.numLegalMoves;
  656.     lglMoves    = (*game)->doc.legalMoves;
  657.  
  658.     oldHndlSize = GetHandleSize((Handle)lglMoves);
  659.     newHndlSize = ((numLglMoves | 0x3F) + 1) * sizeof(MoveElement);
  660.     if (newHndlSize != oldHndlSize)
  661.         SetHandleSize((Handle)lglMoves, newHndlSize);
  662.  
  663.     (**lglMoves)[numLglMoves].moveFrom = from;
  664.     (**lglMoves)[numLglMoves].moveTo   = to;
  665.     (**lglMoves)[numLglMoves].value    = 0;
  666.  
  667.     ++(*game)->doc.numLegalMoves;
  668. }
  669.  
  670.  
  671.  
  672. /*****************************************************************************/
  673.  
  674.  
  675.  
  676. Boolean    CastleOkay(FileRecHndl game, short castleSide)
  677. {
  678.     short    color;
  679.     short    castleDir, kingLoc, rookLoc;
  680.     short    i, j, piece, pieceColor;
  681.     Boolean    kspiece;
  682.  
  683.     color = WhosMove(game);            /* Who's move it is. */
  684.  
  685.     if ((*game)->doc.king[color].kingMoves) return(false);
  686.         /* Can't castle.  King has already moved. */
  687.  
  688.     rookLoc = (kingLoc = (*game)->doc.king[color].kingLoc) + 3;
  689.     if ((castleDir = castleSide) == QSIDE) {
  690.         rookLoc -= 7;
  691.         --castleDir;
  692.     }
  693.  
  694.     for (i = kingLoc, j = 3; j; i += castleDir, --j)
  695.         if (SquareAttacked(game, i, color)) return(false);
  696.             /* Can't castle out of, through, or into check. */
  697.  
  698.     if ((*game)->doc.king[color].rookMoves[castleSide]) return(false);
  699.         /* Rook trying to castle with has already moved,
  700.         ** or has been taken.
  701.         */
  702.  
  703.     piece = (*game)->doc.theBoard[rookLoc];
  704.     kspiece = false;
  705.     if (piece < WK) {
  706.         kspiece = true;
  707.         piece += KSIDEPIECE;
  708.     }
  709.     if (piece > BK) {
  710.         kspiece = true;
  711.         piece -= KSIDEPIECE;
  712.     }
  713.  
  714.     pieceColor = BLACK;
  715.     if (piece < 0) {
  716.         pieceColor = WHITE;
  717.         piece = -piece;
  718.     }
  719.     if (color != pieceColor) return(false);
  720.     if (piece != ROOK)         return(false);
  721.         /* These deviant conditions can occur if user arranged the board. */
  722.  
  723.     /* So far, everything is cool.  The only remaining possible problem is
  724.     ** that there is a piece (or more) between the king and the rook. */
  725.  
  726.     while (kingLoc += castleDir, kingLoc != rookLoc)
  727.         if ((*game)->doc.theBoard[kingLoc]) return(false);
  728.             /* There is a piece in the way, so we can't castle. */
  729.  
  730.     return(true);        /* The castling move is okay. */
  731. }
  732.  
  733.  
  734.  
  735. /*****************************************************************************/
  736.  
  737.  
  738.  
  739. void    MakeMove(FileRecHndl game, short moveFrom, short moveTo, short promoteTo)
  740. {
  741.     GameListHndl    gameMoves;
  742.     long            newHndlSize, oldHndlSize;
  743.     short            gameIndex, numGameMoves, color, rank, i;
  744.     short            pieceMoved, pieceCaptured, pieceCapturedFrom;
  745.     short            absPieceMoved, delta, middle, oldRookLoc;
  746.     Boolean            modifyGame;
  747.  
  748.     if (moveFrom == -1) {
  749.         UnmakeMove(game);
  750.         return;
  751.     }
  752.  
  753.     gameIndex    = (*game)->doc.gameIndex;
  754.     numGameMoves = (*game)->doc.numGameMoves;
  755.     gameMoves    = (*game)->doc.gameMoves;
  756.     color        = WhosMove(game);
  757.  
  758.     i = (gameIndex > numGameMoves) ? gameIndex : numGameMoves;
  759.     newHndlSize = ((i | 0x3F) + 1) * sizeof(GameElement);
  760.     oldHndlSize = GetHandleSize((Handle)gameMoves);
  761.     if (newHndlSize != oldHndlSize)
  762.         SetHandleSize((Handle)gameMoves, newHndlSize);
  763.  
  764.     modifyGame = true;
  765.  
  766.     if (moveFrom == 1) {
  767.         if (gameIndex >= numGameMoves) return;
  768.             /* Already positioned at the end of the game. */
  769.         moveFrom   = (**gameMoves)[gameIndex].moveFrom;
  770.         moveTo     = (**gameMoves)[gameIndex].moveTo;
  771.         promoteTo  = (**gameMoves)[gameIndex].promoteTo;
  772.         modifyGame = false;
  773.     }
  774.  
  775.     if (!moveFrom) {        /* Draw agreed upon, or player resigned. */
  776.         if (gameIndex)
  777.             if (!(**gameMoves)[gameIndex - 1].moveFrom) --gameIndex;
  778.                 /* In case there is a race condition, only allow one game-ending
  779.                 ** "move" to occur. */
  780.         (**gameMoves)[gameIndex].moveFrom          = 0;
  781.         (**gameMoves)[gameIndex].moveTo            = moveTo;
  782.         (**gameMoves)[gameIndex].pieceCaptured     = 0;
  783.         (**gameMoves)[gameIndex].pieceCapturedFrom = 0;
  784.         (**gameMoves)[gameIndex].promoteTo         = 0;
  785.         (*game)->doc.gameIndex = ++gameIndex;
  786.         (*game)->doc.numGameMoves   = gameIndex;
  787.         (*game)->fileState.docDirty = true;
  788.         (*game)->doc.resync = kResync;
  789.         return;
  790.     }
  791.  
  792.     pieceMoved        = (*game)->doc.theBoard[moveFrom];
  793.     pieceCaptured     = (*game)->doc.theBoard[moveTo];
  794.     pieceCapturedFrom = moveTo;
  795.  
  796.     absPieceMoved = (pieceMoved < 0) ? -pieceMoved : pieceMoved;
  797.  
  798.     if (absPieceMoved == PAWN) {
  799.  
  800.         rank = moveTo / 10;
  801.         if ((rank == 2) || (rank == 9)) {
  802.             if (promoteTo < 0) promoteTo = -promoteTo;
  803.             pieceMoved *= promoteTo;
  804.             promoteTo = pieceMoved;
  805.         }
  806.         else promoteTo = 0;
  807.  
  808.         if (moveTo == (*game)->doc.enPasMove) {
  809.             pieceCaptured     = -pieceMoved;
  810.             pieceCapturedFrom = (*game)->doc.enPasPawnLoc;
  811.         }        /* If pawn move is onto en-passant move square, then the
  812.                 ** capture is from a square other than moveTo.
  813.                 */
  814.     }
  815.     else promoteTo = 0;
  816.  
  817.     (**gameMoves)[gameIndex].moveFrom          = moveFrom;
  818.     (**gameMoves)[gameIndex].moveTo            = moveTo;
  819.     (**gameMoves)[gameIndex].pieceCaptured     = pieceCaptured;
  820.     (**gameMoves)[gameIndex].pieceCapturedFrom = pieceCapturedFrom;
  821.     (**gameMoves)[gameIndex].promoteTo         = promoteTo;
  822.     (*game)->doc.gameIndex = ++gameIndex;
  823.  
  824.     if (modifyGame) {
  825.         (*game)->doc.numGameMoves   = gameIndex;
  826.         (*game)->fileState.docDirty = true;
  827.     }
  828.  
  829.     /* The move has now been recorded in the move list.  Now make the move. */
  830.  
  831.     (*game)->doc.theBoard[pieceCapturedFrom] = 0;
  832.     (*game)->doc.theBoard[moveTo]            = pieceMoved;
  833.     (*game)->doc.theBoard[moveFrom]          = 0;
  834.         /* The move is now made, except for the rook if castling. */
  835.  
  836.     delta  = moveTo - moveFrom;
  837.     middle = (moveTo + moveFrom) / 2;
  838.  
  839.     if (absPieceMoved == KING) {
  840.         oldRookLoc = 0;
  841.         if (delta == -2) oldRookLoc = moveFrom - 4;
  842.         if (delta == 2)  oldRookLoc = moveFrom + 3;
  843.         if (oldRookLoc) {
  844.             (*game)->doc.theBoard[middle] = (*game)->doc.theBoard[oldRookLoc];
  845.             (*game)->doc.theBoard[oldRookLoc] = 0;
  846.         }
  847.     }        /* Castling (and therefore move) now complete. */
  848.  
  849.  
  850.     /* All that remains is some information updating for castling, king
  851.     ** position, and en-passant. */
  852.  
  853.     if (absPieceMoved == KING) {
  854.         (*game)->doc.king[color].kingLoc = moveTo;
  855.         ++(*game)->doc.king[color].kingMoves;
  856.     }
  857.  
  858.     if ((moveFrom==21) || (moveTo==21)) ++(*game)->doc.king[BLACK].rookMoves[QSIDE];
  859.     if ((moveFrom==28) || (moveTo==28)) ++(*game)->doc.king[BLACK].rookMoves[KSIDE];
  860.     if ((moveFrom==91) || (moveTo==91)) ++(*game)->doc.king[WHITE].rookMoves[QSIDE];
  861.     if ((moveFrom==98) || (moveTo==98)) ++(*game)->doc.king[WHITE].rookMoves[KSIDE];
  862.         /* This accounts for all rook moves/captures, other than castling.
  863.         ** This is necessary to keep track of rook moves/captures to determine
  864.         ** if castling is allowed.  Rook moves when castling don't have to be
  865.         ** accounted for, since the king move in the castle will prevent
  866.         ** any more castling.
  867.         */
  868.  
  869.     /* If the move was a double-pawn-push, then we have some en-passant
  870.     ** information to record.  Otherwise, we need to zero-out these values. */
  871.  
  872.     if (absPieceMoved == PAWN) {
  873.  
  874.         if ((delta != -20) && (delta != 20))
  875.             middle = moveTo = 0;
  876.                 /* If not a double-pawn-push, then record 0's for the
  877.                 ** en-passant information.  This will prevent en-passant
  878.                 ** moves from being generated. */
  879.  
  880.     }
  881.     else middle = moveTo = 0;
  882.  
  883.     (*game)->doc.enPasMove    = middle;
  884.     (*game)->doc.enPasPawnLoc = moveTo;
  885.         /* Record the en-passant information.  These values are
  886.         ** non-zero if a pawn was double-pushed. */
  887.  
  888. }
  889.  
  890.  
  891.  
  892. /*****************************************************************************/
  893.  
  894.  
  895.  
  896. void    UnmakeMove(FileRecHndl game)
  897. {
  898.     GameListHndl    gameMoves;
  899.     short            gameIndex, numGameMoves;
  900.     short            moveFrom, moveTo, pieceCaptured, pieceCapturedFrom;
  901.     short            promoteTo, pieceMoved, color, delta, oldRookLoc, middle;
  902.  
  903.     gameIndex    = (*game)->doc.gameIndex;
  904.     numGameMoves = (*game)->doc.numGameMoves;
  905.     gameMoves    = (*game)->doc.gameMoves;
  906.  
  907.     if (!gameIndex) return;
  908.     --gameIndex;
  909.  
  910.     moveFrom          = (**gameMoves)[gameIndex].moveFrom;
  911.     moveTo            = (**gameMoves)[gameIndex].moveTo;
  912.     pieceCaptured     = (**gameMoves)[gameIndex].pieceCaptured;
  913.     pieceCapturedFrom = (**gameMoves)[gameIndex].pieceCapturedFrom;
  914.     promoteTo         = (**gameMoves)[gameIndex].promoteTo;
  915.  
  916.     if (moveFrom) {        /* The "move" could be a resign or draw.  Make sure it isn't. */
  917.         pieceMoved = (*game)->doc.theBoard[moveTo];
  918.         if (promoteTo) pieceMoved = (pieceMoved < 0) ? -1 : 1;
  919.  
  920.         (*game)->doc.theBoard[moveFrom]          = pieceMoved;
  921.         (*game)->doc.theBoard[moveTo]            = 0;
  922.         (*game)->doc.theBoard[pieceCapturedFrom] = pieceCaptured;
  923.             /* Any move now undone, except for castling.  The rook still has
  924.             ** to be put back. */
  925.  
  926.         if ((pieceMoved == KING) || (pieceMoved == -KING)) {
  927.  
  928.             color = ((gameIndex + (*game)->doc.startColor) & 0x01);        /* Who's move it is. */
  929.             (*game)->doc.king[color].kingLoc = moveFrom;
  930.             --(*game)->doc.king[color].kingMoves;
  931.  
  932.             delta  = moveTo - moveFrom;
  933.             oldRookLoc = 0;
  934.             if (delta == -2) oldRookLoc = moveFrom - 4;
  935.             if (delta == 2)  oldRookLoc = moveFrom + 3;
  936.             if (oldRookLoc) {
  937.                 middle = (moveTo + moveFrom) / 2;
  938.                 (*game)->doc.theBoard[oldRookLoc] = (*game)->doc.theBoard[middle];
  939.                 (*game)->doc.theBoard[middle] = 0;
  940.             }
  941.         }        /* Castling now completely undone. */
  942.  
  943.         if ((moveFrom == 21) || (moveTo == 21)) --(*game)->doc.king[BLACK].rookMoves[QSIDE];
  944.         if ((moveFrom == 28) || (moveTo == 28)) --(*game)->doc.king[BLACK].rookMoves[KSIDE];
  945.         if ((moveFrom == 91) || (moveTo == 91)) --(*game)->doc.king[WHITE].rookMoves[QSIDE];
  946.         if ((moveFrom == 98) || (moveTo == 98)) --(*game)->doc.king[WHITE].rookMoves[KSIDE];
  947.             /* Undo any rook move/capture accounting.  This info is used when
  948.             ** determining of castling is allowed.
  949.             */
  950.  
  951.         (*game)->doc.enPasMove = (*game)->doc.enPasPawnLoc = 0;
  952.             /* Assume move previous to the one we just undid was not a
  953.             ** double-pawn-push.  If it was not a double-pawn-push, then
  954.             ** en-passant moves are not possible at the move number we
  955.             ** just undid.
  956.             */
  957.     }
  958.  
  959.     if (gameIndex) {        /* Restore en-passant possibilities. */
  960.  
  961.         moveFrom   = (**gameMoves)[--gameIndex].moveFrom;
  962.         moveTo     = (**gameMoves)[gameIndex++].moveTo;
  963.         pieceMoved = (*game)->doc.theBoard[moveTo];
  964.  
  965.         if ((pieceMoved == PAWN) || (pieceMoved == -PAWN)) {
  966.             delta = moveTo - moveFrom;
  967.             if ((delta == -20) || (delta == 20)) {
  968.                 (*game)->doc.enPasMove    = (moveTo + moveFrom) / 2;
  969.                 (*game)->doc.enPasPawnLoc = moveTo;
  970.             }
  971.         }
  972.     }
  973.     else {
  974.         (*game)->doc.enPasMove    = (*game)->doc.arngEnPasMove;
  975.         (*game)->doc.enPasPawnLoc = (*game)->doc.arngEnPasPawnLoc;
  976.     }
  977.  
  978.     (*game)->doc.gameIndex = gameIndex;
  979. }
  980.  
  981.  
  982.  
  983. /*****************************************************************************/
  984.  
  985.  
  986.  
  987. short    SquareAttacked(FileRecHndl game, short square, short color)
  988. {
  989.     short    destColor;
  990.     short    kind;
  991.     short    dirNum, dir;
  992.     short    s, dist, maxDist, dest, taker, takerLoc;
  993.  
  994.     taker    = KING + 1;        /* This is a no-take flag. */
  995.     takerLoc = 0;
  996.  
  997.     for (kind = KNIGHT; kind <= QUEEN; kind += (QUEEN - KNIGHT)) {
  998.         /* Check in the knight and queen directions. */
  999.  
  1000.         for (dirNum = 0; (dir = direction[kind][dirNum]) != 0; ++dirNum) {
  1001.             /* The direction we will scan for an attack, for the most part. */
  1002.  
  1003.             maxDist = (kind == KNIGHT) ? 1 : 7;
  1004.             for (s = square, dist = 1; dist <= maxDist; ++dist) {
  1005.  
  1006.                 s += dir;
  1007.                 if ((dest = (*game)->doc.theBoard[s]) == EMPTY) continue;
  1008.                     /* Empty square, so keep looking. */
  1009.  
  1010.                 if (dest == OBNDS) break;
  1011.                     /* Can't be attacked from this direction anymore. */
  1012.  
  1013.                 destColor = BLACK;
  1014.                 if (dest < 0) {
  1015.                     destColor = WHITE;
  1016.                     dest = -dest;
  1017.                 }
  1018.                 if (dest > BK) dest -= KSIDEPIECE;
  1019.  
  1020.                 if (destColor == color) break;
  1021.                     /* Ran into our own piece, so no attack
  1022.                     ** from this direction.
  1023.                     */
  1024.  
  1025.                 if (dest >= taker) continue;
  1026.  
  1027.                 if (kind == KNIGHT)    {        /* If we are looking for knights... */
  1028.                     if (dest == KNIGHT) {
  1029.                         taker    = KNIGHT;
  1030.                         takerLoc = s;
  1031.                         dirNum   = 7;        /* Since there is 'an' attack by a knight,
  1032.                                             ** we don't care if there is another.
  1033.                                             ** We have already established a knight
  1034.                                             ** take, so we can skip the rest of the
  1035.                                             ** knight directions. */
  1036.                     }
  1037.                     continue;        /* Only knights can take in this direction. */
  1038.                 }
  1039.  
  1040.                 if (dest == KING) {
  1041.                     if (dist == 1) {
  1042.                         taker    = KING;
  1043.                         takerLoc = s;
  1044.                     }
  1045.                     break;
  1046.                 }            /* We are looking in the non-knight move directions
  1047.                             ** for attackers.  If a king is found as the possible
  1048.                             ** attacker, make sure it is in range (one square away)
  1049.                             ** before counting it as an attack.  If it isn't one
  1050.                             ** square away, then it serves to prevent any other
  1051.                             ** attack from this direction.
  1052.                             */
  1053.  
  1054.                 if (dest == QUEEN) {
  1055.                     taker    = QUEEN;
  1056.                     takerLoc = s;
  1057.                     break;
  1058.                 }            /* If the potential attacker is a queen, then it is
  1059.                             ** a valid attacker.
  1060.                             */
  1061.  
  1062.                 if (dest == ROOK) {
  1063.                     if (dirNum > 3) {
  1064.                         taker    = ROOK;
  1065.                         takerLoc = s;
  1066.                     }
  1067.                     break;
  1068.                 }            /* If the potential attacker is a rook, and we are
  1069.                             ** examining a rank or file, then count it as an attacker.
  1070.                             ** Otherwise, the rook serves to prevent any other
  1071.                             ** attack from this direction.
  1072.                             */
  1073.  
  1074.                 if (dest == BISHOP) {
  1075.                     if (dirNum < 4) {
  1076.                         taker    = BISHOP;
  1077.                         takerLoc = s;
  1078.                     }
  1079.                     break;
  1080.                 }            /* If the potential attacker is a bishop, and we are
  1081.                             ** examining a diagonal, then count it as an attacker.
  1082.                             ** Otherwise, the bishop serves to prevent any other
  1083.                             ** attack from this direction.
  1084.                             */
  1085.  
  1086.                 if (dest == PAWN) {
  1087.                     if (destColor == BLACK) {
  1088.                         if ((dirNum < 2) && (dist == 1)) {
  1089.                             taker    = PAWN;
  1090.                             takerLoc = s;
  1091.                         }
  1092.                     }
  1093.                     else
  1094.                         if (
  1095.                             (dirNum > 1) && 
  1096.                             (dirNum < 4) && 
  1097.                             (dist == 1)
  1098.                         ) {
  1099.                             taker    = PAWN;
  1100.                             takerLoc = s;
  1101.                         }
  1102.                     break;
  1103.                 }
  1104.  
  1105.                 break;
  1106.                     /* Final case is a knight in a non-knight direction. */
  1107.             }
  1108.         }
  1109.     }
  1110.  
  1111.     return(takerLoc);
  1112. }
  1113.  
  1114.  
  1115.  
  1116. /*****************************************************************************/
  1117.  
  1118.  
  1119.  
  1120. void    EndTheGame(FileRecHndl game, short endReason)
  1121. {
  1122.     WindowPtr    oldPort;
  1123.  
  1124.     MakeMove(game, 0, endReason, 0);
  1125.         /* Record who resigned or draw agreement. */
  1126.  
  1127.     oldPort = SetFilePort(game);
  1128.     ImageDocument(game, true);
  1129.     AdjustGameSlider(game);
  1130.     UpdateGameStatus(game);
  1131.     SetPort(oldPort);
  1132. }
  1133.  
  1134.  
  1135.  
  1136. /*****************************************************************************/
  1137.  
  1138.  
  1139.  
  1140. short    WhosMove(FileRecHndl game)
  1141. {
  1142.     short    color;
  1143.  
  1144.     color  = ((*game)->doc.gameIndex ^ (*game)->doc.startColor);
  1145.     return(color & 0x01);
  1146. }
  1147.  
  1148.  
  1149.  
  1150. /*****************************************************************************/
  1151.  
  1152.  
  1153.  
  1154. short    GameStatus(FileRecHndl game)
  1155. {
  1156.     short            i, color, kingLoc;
  1157.     short            board[120], *boardPtr;
  1158.     short            origGameIndex, gameIndex;
  1159.     short            rep, back, pieceMoved, gameStat;
  1160.     GameListHndl    gameMoves;
  1161.  
  1162.     gPosReps = 0;
  1163.  
  1164.     if ((*game)->doc.arrangeBoard) return(kGameContinues);
  1165.  
  1166.     for (i = 0; i < 2; ++i)
  1167.         if (!(*game)->doc.timeLeft[i])
  1168.             return(kYouLoseOnTime - (i ^ (*game)->doc.myColor));
  1169.  
  1170.     GenerateLegalMoves(game);
  1171.  
  1172.     gameMoves     = (*game)->doc.gameMoves;
  1173.     origGameIndex = (*game)->doc.gameIndex;
  1174.  
  1175.     if (!(*game)->doc.numLegalMoves) {
  1176.  
  1177.         color = WhosMove(game);            /* Who's move it is. */
  1178.  
  1179.         if ((origGameIndex) && (origGameIndex == (*game)->doc.numGameMoves))
  1180.             if (!(**gameMoves)[origGameIndex - 1].moveFrom)
  1181.                 return((**gameMoves)[origGameIndex - 1].moveTo);
  1182.  
  1183.         kingLoc = (*game)->doc.king[color].kingLoc;
  1184.  
  1185.         if (SquareAttacked(game, kingLoc, color)) {        /* Checkmated. */
  1186.             if (color == (*game)->doc.myColor) return(kYouLose);
  1187.             else return(kYouWin);
  1188.         }
  1189.  
  1190.         return(kStalemate);
  1191.     }
  1192.  
  1193.     BlockMove((Ptr)&(*game)->doc.theBoard[0], (Ptr)&board[0], 120 * sizeof(short));
  1194.  
  1195.     rep = 2;            /* 2 matching current position makes 3 that match. */
  1196.     back = 100;
  1197.  
  1198.     gameIndex = (*game)->doc.gameIndex;
  1199.     while ((rep) && (back) && (gameIndex)) {
  1200.  
  1201.         if ((**gameMoves)[--gameIndex].pieceCaptured) break;
  1202.  
  1203.         pieceMoved = (*game)->doc.theBoard[(**gameMoves)[gameIndex].moveTo];
  1204.         if (pieceMoved < 0) pieceMoved = -pieceMoved;
  1205.         if (pieceMoved == PAWN) break;
  1206.  
  1207.         UnmakeMove(game);
  1208.         --back;
  1209.  
  1210.         boardPtr = &(*game)->doc.theBoard[0];
  1211.         for (i = START_IBNDS; i < END_IBNDS; ++i) if (boardPtr[i] != board[i]) break;
  1212.  
  1213.         if (i == END_IBNDS) {
  1214.             ++gPosReps;
  1215.             if (!(--rep)) break;
  1216.         }
  1217.     }
  1218.  
  1219.     while ((*game)->doc.gameIndex != origGameIndex) MakeMove(game, 1, 0, 0);
  1220.  
  1221.     gameStat = kGameContinues;
  1222.     if (!rep)  gameStat = kDrawByRep;
  1223.     if (!back) gameStat = kDrawBy50;
  1224.  
  1225.     if (gameStat) (*game)->doc.numLegalMoves = 0;
  1226.  
  1227.     return(gameStat);
  1228. }
  1229.  
  1230.  
  1231.  
  1232. /*****************************************************************************/
  1233.  
  1234.  
  1235.  
  1236. short    UpdateTime(FileRecHndl game, Boolean canLose)
  1237. {
  1238.     FileRecPtr    frPtr;
  1239.     short        moveColor, myColor;
  1240.     long        timeLeft, opponentTimeLeft, oldTimeLeft, diff;
  1241.  
  1242.     frPtr = *game;
  1243.     moveColor        = WhosMove(game);
  1244.     myColor          = frPtr->doc.myColor;
  1245.     timeLeft         = frPtr->doc.timeLeft[moveColor];
  1246.     opponentTimeLeft = frPtr->doc.timeLeft[moveColor ^ 1];
  1247.  
  1248.     if (!frPtr->doc.twoPlayer) myColor = moveColor;
  1249.         /* Since we are not playing over the net, both sides can lose 
  1250.         ** due to time on this machine. */
  1251.  
  1252.     if ((timeLeft > 0) && (opponentTimeLeft > 0)) {
  1253.         oldTimeLeft = timeLeft;
  1254.         diff = TickCount() - frPtr->doc.timerRefTick;
  1255.         if (diff < 0) {
  1256.             frPtr->doc.timerRefTick = TickCount();
  1257.             diff = 0;
  1258.         }
  1259.         diff /= 60;
  1260.         diff *= 60;
  1261.         if (diff >= 60) {
  1262.             if (!GameStatus(game)) {
  1263.                 timeLeft -= diff;
  1264.                 if (timeLeft < 60) timeLeft = 0;
  1265.                 if (!timeLeft)
  1266.                     if ((myColor != moveColor) || (!canLose)) timeLeft = 60;
  1267.                 frPtr->doc.timeLeft[moveColor] = timeLeft;
  1268.                 frPtr->doc.timerRefTick += diff;
  1269.                 if (!timeLeft) return(2);
  1270.                 if (timeLeft != oldTimeLeft) return(1);
  1271.             }
  1272.             else (*game)->doc.timerRefTick = TickCount();
  1273.                 /* Someone has already lost, so no time change. */
  1274.         }
  1275.     }
  1276.     else (*game)->doc.timerRefTick = TickCount();
  1277.  
  1278.     return(0);
  1279. }
  1280.  
  1281.  
  1282.  
  1283. /*****************************************************************************/
  1284.  
  1285.  
  1286.  
  1287. void    UpdateGameStatus(FileRecHndl game)
  1288. {
  1289.     WindowPtr        oldPort;
  1290.     ControlHandle    draw, resign;
  1291.     short            status, myColor;
  1292.     Rect            drawRect, resignRect, workRect;
  1293.     Point            endOfText;
  1294.     Boolean            hideEm, hidden;
  1295.     Str255            reasonText;
  1296.  
  1297.     if ((*game)->doc.arrangeBoard) return;
  1298.  
  1299.     draw   = (*game)->doc.draw;
  1300.     resign = (*game)->doc.resign;
  1301.  
  1302.     if (!draw) return;
  1303.  
  1304.     oldPort = SetFilePort(game);
  1305.  
  1306.     drawRect   = (*draw)->contrlRect;
  1307.     resignRect = (*resign)->contrlRect;
  1308.  
  1309.     hideEm = false;
  1310.     status = GameStatus(game);
  1311.     if (status) hideEm = true;
  1312.  
  1313.     hidden = false;
  1314.     if (drawRect.top & 0x4000) hidden = true;
  1315.  
  1316.     workRect = drawRect;
  1317.     if (hidden) OffsetRect(&workRect, 0, -0x4000);
  1318.     workRect.right = resignRect.right;
  1319.  
  1320.     if (hideEm != hidden) {
  1321.         EraseRect(&workRect);
  1322.         MoveControl(draw,   drawRect.left,   drawRect.top ^ 0x4000);
  1323.         MoveControl(resign, resignRect.left, resignRect.top ^ 0x4000);
  1324.     }
  1325.  
  1326.     if (hideEm) {
  1327.         myColor = (*game)->doc.myColor;
  1328.         switch (status) {
  1329.             case kYouWin:
  1330.             case kYouWinOnTime:
  1331.                 status = kWhiteWins + myColor;
  1332.                 break;
  1333.             case kYouLose:
  1334.             case kYouLoseOnTime:
  1335.                 status = kBlackWins - myColor;
  1336.                 break;
  1337.         }
  1338.  
  1339.         TextMode(srcCopy);
  1340.         TextFont(systemFont);
  1341.         TextSize(0);
  1342.         GetIndString(reasonText, rGameStat, status);
  1343.         MoveTo(workRect.left, workRect.top + 14);
  1344.         DrawString(reasonText);
  1345.         TextMode(srcOr);
  1346.         GetPen(&endOfText);
  1347.         workRect.left = endOfText.h;
  1348.         EraseRect(&workRect);
  1349.     }
  1350.  
  1351.     SetPort(oldPort);
  1352. }
  1353.  
  1354.  
  1355.  
  1356. /*****************************************************************************/
  1357.  
  1358.  
  1359.  
  1360. void    DrawButtonTitle(FileRecHndl game, short newVal)
  1361. {
  1362.     WindowPtr        oldPort;
  1363.     ControlHandle    draw;
  1364.     Rect            drawRect;
  1365.     Str255            drawButtonText;
  1366.  
  1367.     if (newVal != (*game)->doc.drawBtnState) {
  1368.  
  1369.         oldPort = SetFilePort(game);
  1370.  
  1371.         GetIndString(drawButtonText, rGameStat, kDrawButtonText + newVal);
  1372.  
  1373.         draw = (*game)->doc.draw;
  1374.         SetControlTitle(draw, drawButtonText);
  1375.         (*game)->doc.drawBtnState = newVal;
  1376.  
  1377.         drawRect = (*draw)->contrlRect;
  1378.         ValidRect(&drawRect);
  1379.  
  1380.         SetPort(oldPort);
  1381.     }
  1382. }
  1383.  
  1384.  
  1385.  
  1386. /*****************************************************************************/
  1387. /*****************************************************************************/
  1388.  
  1389.  
  1390.  
  1391. Boolean    ComputerMove(FileRecHndl game)
  1392. {
  1393.     OSErr            err;
  1394.     FileRecHndl        workGame;
  1395.     short            numLegalMoves, moveNum, fromSq, toSq, i;
  1396.     short            wtotal, btotal, color, update;
  1397.     WindowPtr        window;
  1398.     Boolean            compMoved;
  1399.     MoveListHndl    legalMoves;
  1400.  
  1401.     compMoved = false;
  1402.  
  1403.     IncNewFileNum(false);
  1404.     err = AppDuplicateDocument(game, &workGame);
  1405.     IncNewFileNum(true);
  1406.     if (err) return(false);
  1407.  
  1408.     for (i = 0; i < 2; ++i) (*workGame)->doc.timeLeft[i] = (*game)->doc.timeLeft[i];
  1409.  
  1410.     GetPort(&window);
  1411.     (*workGame)->fileState.window = window;
  1412.  
  1413.     GenerateLegalMoves(game);
  1414.     GenerateLegalMoves(workGame);
  1415.  
  1416.     numLegalMoves = (*workGame)->doc.numLegalMoves;
  1417.     legalMoves    = (*workGame)->doc.legalMoves;
  1418.  
  1419.     if (numLegalMoves) {        /* If there is a move, pick one, any one. */
  1420.         if (numLegalMoves == 1) moveNum = 0;
  1421.         else {
  1422.             moveNum = CheckForMate(workGame, 0, 2);
  1423.             if (moveNum == -1) {
  1424.                 CalcPositionValues(workGame);
  1425.                 wtotal = gWhiteTotal >> 16;
  1426.                 btotal = gBlackTotal >> 16;
  1427.                 color  = WhosMove(workGame);
  1428.                 for (;;) {
  1429.                     if (gNumPieces == 1) {
  1430.                         if (
  1431.                             ((color == WHITE) && (wtotal == 9)) ||
  1432.                             ((color == BLACK) && (btotal == 9))
  1433.                         ) {
  1434.                             moveNum = QueenMate(workGame);
  1435.                             break;
  1436.                         }
  1437.                         if (
  1438.                             ((color == WHITE) && (wtotal == 5)) ||
  1439.                             ((color == BLACK) && (btotal == 5))
  1440.                         ) {
  1441.                             moveNum = RookMate(workGame);
  1442.                             break;
  1443.                         }
  1444.                     }
  1445.                     moveNum = BestMove(workGame);
  1446.                     break;
  1447.                 }
  1448.             }
  1449.         }
  1450.  
  1451.         if (moveNum > -1) {
  1452.             update = UpdateTime(game, true);
  1453.             if (update) DrawTime(game);
  1454.             if (update == 2) {
  1455.                 if ((*game)->doc.twoPlayer) SendGame(game, kIsMove, nil);
  1456.                 AlertIfGameOver(game);
  1457.             }
  1458.             else {
  1459.                 fromSq = (**legalMoves)[moveNum].moveFrom;
  1460.                 toSq   = (**legalMoves)[moveNum].moveTo;
  1461.                 SlideThePiece(game, fromSq, toSq);
  1462.                 MakeMove(game, fromSq, toSq, QUEEN);
  1463.                 compMoved = true;
  1464.             }
  1465.         }
  1466.         if (moveNum == kComputerResigns) {
  1467.             EndTheGame(game, kWhiteResigns + WhosMove(game));
  1468.             if ((*game)->doc.twoPlayer) SendGame(game, kIsMove, nil);
  1469.             gComputerResigns = true;
  1470.             AlertIfGameOver(game);
  1471.         }
  1472.     }
  1473.  
  1474.     AppDisposeDocument(workGame);
  1475.     return(compMoved);
  1476. }
  1477.  
  1478.  
  1479.  
  1480. /*****************************************************************************/
  1481.  
  1482.  
  1483.  
  1484. short    CheckForMate(FileRecHndl game, short nodeDepth, short maxDepth)
  1485. {
  1486.     short            num, ourMove, move;
  1487.     short            color, kingLoc, result;
  1488.     MoveListHndl    node;
  1489.     EventRecord        event;
  1490.  
  1491.     ourMove = -1;
  1492.     GenerateLegalMoves(game);
  1493.  
  1494.     num  = (*game)->doc.numLegalMoves;
  1495.     node = (*game)->doc.legalMoves;
  1496.  
  1497.     (*game)->doc.legalMoves = gNodeHndl[nodeDepth];
  1498.         /* Protect the list of legal moves for this level.  Put a handle
  1499.         ** into the game where moves for the next level can be placed. */
  1500.  
  1501.     color = WhosMove(game) ^ 1;
  1502.     if (!(nodeDepth & 0x01)) {
  1503.         for (move = 0; (move < num) && (ourMove == -1); ++move) {
  1504.             MakeMove(game, (**node)[move].moveFrom, (**node)[move].moveTo, QUEEN);
  1505.             kingLoc = (*game)->doc.king[color].kingLoc;
  1506.             if (SquareAttacked(game, kingLoc, color)) {        /* The move caused check. */
  1507.                 GenerateLegalMoves(game);
  1508.                 if (!(*game)->doc.numLegalMoves) {            /* If no way out of check... */
  1509.                     ourMove = move;                            /* ...it is checkmate. */
  1510.                 }
  1511.             }
  1512.             UnmakeMove(game);
  1513.         }
  1514.         if (idleTick + 10 < TickCount()) {
  1515.             idleTick = TickCount();
  1516.             if (EventAvail(everyEvent - highLevelEventMask, &event)) ourMove = -2;
  1517.             else {
  1518.                 CTEIdle();
  1519.                 DoIdleTasks(false);
  1520.             }
  1521.         }
  1522.     }
  1523.  
  1524.     if (nodeDepth < maxDepth) {
  1525.         if (ourMove == -1) {
  1526.             for (move = 0; (move < num) && (ourMove == -1); ++move) {
  1527.                 MakeMove(game, (**node)[move].moveFrom, (**node)[move].moveTo, QUEEN);
  1528.                 result = CheckForMate(game, nodeDepth + 1, maxDepth);
  1529.                 if (result == -1) {
  1530.                     ourMove = move;
  1531.                 }
  1532.                     /* If opponent can't find saving move, then this is the move we want. */
  1533.                 if (result == -2) ourMove = -2;
  1534.                     /* User interrupted search. */
  1535.                 UnmakeMove(game);
  1536.             }
  1537.         }
  1538.     }
  1539.  
  1540.     if (!nodeDepth) {
  1541.         if (ourMove > -1) {
  1542.             MakeMove(game, (**node)[ourMove].moveFrom, (**node)[ourMove].moveTo, QUEEN);
  1543.             kingLoc = (*game)->doc.king[color].kingLoc;
  1544.             if (!SquareAttacked(game, kingLoc, color)) {    /* It is not a mate in 1. */
  1545.                 GenerateLegalMoves(game);
  1546.                 if (!(*game)->doc.numLegalMoves) ourMove = -1;
  1547.                     /* It is a stalemate.  Throw it back. */
  1548.             }
  1549.             UnmakeMove(game);
  1550.         }
  1551.     }
  1552.  
  1553.     (*game)->doc.numLegalMoves = num;
  1554.     (*game)->doc.legalMoves    = node;
  1555.  
  1556.     return(ourMove);
  1557. }
  1558.  
  1559.  
  1560.  
  1561. /*****************************************************************************/
  1562.  
  1563.  
  1564.  
  1565. short    BestMove(FileRecHndl game)
  1566. {
  1567.     short            bestMove, keepBestMove, num;
  1568.     short            i, color, move, from, to;
  1569.     Boolean            playOn;
  1570.     long            value, max;
  1571.     MoveListHndl    node;
  1572.     EventRecord        event;
  1573.  
  1574.     bestMove = -1;
  1575.  
  1576.     num  = (*game)->doc.numLegalMoves;
  1577.     node = (*game)->doc.legalMoves;
  1578.  
  1579.     (*game)->doc.legalMoves = gNodeHndl[kLastNode];
  1580.         /* Protect the list of legal moves for this node.  Put a different handle
  1581.         ** into the game where moves for the next level can be placed. */
  1582.  
  1583.     color  = WhosMove(game);
  1584.     playOn = false;
  1585.     if ((i = (*game)->doc.timeLeft[1 - color]) >= 0)
  1586.         if (i < 7200)
  1587.             playOn = true;
  1588.  
  1589.     for (max = 0x80000000L, move = 0; move < num; ++move) {
  1590.         from = (**node)[move].moveFrom;
  1591.         to   = (**node)[move].moveTo;
  1592.         (**node)[move].value = value = OneDeepEval(game, from, to, color);
  1593.         if (max < value) {
  1594.             max = value;
  1595.             bestMove = move;        /* Best move so far. */
  1596.         }
  1597.         if (idleTick + 10 < TickCount()) {
  1598.             idleTick = TickCount();
  1599.             if (EventAvail(everyEvent - highLevelEventMask, &event)) {
  1600.                 bestMove = -2;
  1601.                 break;
  1602.             }
  1603.             CTEIdle();
  1604.             DoIdleTasks(false);
  1605.         }
  1606.     }
  1607.  
  1608.     if (bestMove > -1) {        /* Make sure we aren't getting mated in two. */
  1609.         for (keepBestMove = bestMove;;) {
  1610.             from = (**node)[bestMove].moveFrom;
  1611.             to   = (**node)[bestMove].moveTo;
  1612.             MakeMove(game, from, to, QUEEN);
  1613.             i = CheckForMate(game, 0, 2);    /* Check for mate in 2. */
  1614.             UnmakeMove(game);
  1615.             if (i == -2) {
  1616.                 bestMove = -2;
  1617.                 break;        /* User wants to do something, so interrupt. */
  1618.             }
  1619.             if (i == -1) {
  1620.                 max = (**node)[bestMove].value;
  1621.                 if (!playOn)
  1622.                     if (max < -0x00070000)
  1623.                         if (!(Random() & 0x03))
  1624.                             bestMove = kComputerResigns;    /* Resign sometimes. */
  1625.                 break;        /* Opponent has no mate in 2 against bestMove. */
  1626.             }
  1627.             (**node)[bestMove].value = max = 0x80000000L;    /* Getting mated is bad-bad. */
  1628.             for (i = 0; i < num; ++i) {        /* Try the next best move. */
  1629.                 if (max < (**node)[i].value) {
  1630.                     max = (**node)[i].value;
  1631.                     bestMove = i;
  1632.                 }
  1633.             }
  1634.             if (max == 0x80000000L) {
  1635.                 bestMove = keepBestMove;    /* We are going to get mated.  (Bummer.) */
  1636.                 if (!playOn)
  1637.                     if (!(Random() & 0x01))
  1638.                         bestMove = kComputerResigns;    /* Resign sometimes. */
  1639.                 break;
  1640.             }
  1641.         }
  1642.     }
  1643.  
  1644.     (*game)->doc.numLegalMoves = num;
  1645.     (*game)->doc.legalMoves    = node;
  1646.  
  1647.     return(bestMove);
  1648. }
  1649.  
  1650.  
  1651.  
  1652. /*****************************************************************************/
  1653.  
  1654.  
  1655.  
  1656. long    OneDeepEval(FileRecHndl game, short from, short to, short color)
  1657. {
  1658.     short            material, matBalance, xcngVal, posVal;
  1659.     short            take, takerLoc, retakerLoc;
  1660.     short            val, saveBoard;
  1661.     short            *boardPtr, keepBoard[120], square, j, k, xcolor;
  1662.     short            movedPiece, piece, pieceColor, r, c, cc, delta, xcng[64], xnum, loop;
  1663.     short            dirNum, dir, s, dist, numChecks;
  1664.     long            value, v1, v2, v3;
  1665.  
  1666.     MakeMove(game, from, to, QUEEN);
  1667.  
  1668.     boardPtr   = &(*game)->doc.theBoard[0];
  1669.     matBalance = xcngVal = posVal = 0;
  1670.  
  1671.     for (material = 0, square = START_IBNDS; square < END_IBNDS; ++square) {
  1672.         piece = boardPtr[square];
  1673.         if (piece) {
  1674.             if (piece != OBNDS) {
  1675.                 if (piece < 0) piece = -piece;
  1676.                 if (piece > BK) piece -= KSIDEPIECE;
  1677.                 if (piece == KING)   piece = 0;
  1678.                 if (piece == QUEEN)  piece = 9;
  1679.                 if (piece == ROOK)   piece = 5;
  1680.                 if (piece == KNIGHT) piece = 3;
  1681.                 material += piece;
  1682.             }
  1683.         }
  1684.     }
  1685.  
  1686.     movedPiece = boardPtr[to];
  1687.     if (movedPiece < 0) movedPiece = -movedPiece;
  1688.     if (movedPiece > BK) movedPiece -= KSIDEPIECE;
  1689.  
  1690.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  1691.  
  1692.         piece = boardPtr[square];
  1693.         if (piece) {        /* Evaluate value of this piece being here. */
  1694.  
  1695.             if (piece != OBNDS) {
  1696.  
  1697.                 pieceColor = BLACK;    /* Figure who's piece it is. */
  1698.                 if (piece < 0) {
  1699.                     pieceColor = WHITE;
  1700.                     piece = -piece;
  1701.                 }
  1702.                 if (piece > BK) piece -= KSIDEPIECE;
  1703.  
  1704.                 j = piece;
  1705.                 if (j == QUEEN)  j = 9;
  1706.                 if (j == ROOK)   j = 5;
  1707.                 if (j == KNIGHT) j = 3;
  1708.                 if (color == pieceColor) matBalance += j;
  1709.                 else                     matBalance -= j;
  1710.  
  1711.                 r = square / 10;    /* Get row and column of piece. */
  1712.                 c = square - 10 * r - 1;
  1713.                 r -= 2;
  1714.  
  1715.                 if (pieceColor == WHITE) r = 7 - r;        /* Flip rank for white. */
  1716.  
  1717.                 if (piece == PAWN) {            /* Weight the pawn position. */
  1718.                     if (r > 4) {
  1719.                         if (r == 6) r = 63;        /* Highly advanced pawns are nice. */
  1720.                         if (r == 5) r = 30;
  1721.                     }
  1722.                     else {
  1723.                         if (material > 40) {
  1724.                             cc = c;
  1725.                             if (cc > 3) cc = 7 - cc;
  1726.                             if (r < 2) cc = 0;
  1727.                             switch (cc) {
  1728.                                 case 0:
  1729.                                     r = 0;
  1730.                                     break;
  1731.                                 case 1:
  1732.                                     if (r > 2) r = -8;
  1733.                                     else       r = 0;
  1734.                                     break;
  1735.                                 case 2:
  1736.                                     if (r == 2) r = -2;
  1737.                                     if (r == 3) {
  1738.                                         r = 6;
  1739.                                         if (c < 5) r = 11;
  1740.                                     }
  1741.                                     break;
  1742.                                 case 3:
  1743.                                     if (r == 2) r = 6;
  1744.                                     if (r == 3) {
  1745.                                         r = 6;
  1746.                                         if (c < 5) r = 11;
  1747.                                     }
  1748.                                     break;
  1749.                             }
  1750.                         }
  1751.                         if (material < 26) r *= 4;
  1752.                     }
  1753.                     for (j = 9; j <= 11; j += 2) {    /* Give weight to pawn chains. */
  1754.                         k = boardPtr[square + j];
  1755.                         if (pieceColor == WHITE) k = -k;
  1756.                         if (k == PAWN) {
  1757.                             k = boardPtr[square - j];
  1758.                             if (pieceColor == WHITE) k = -k;
  1759.                             if (k == PAWN) ++r;
  1760.                         }
  1761.                     }
  1762.                     for (j = square;;) {    /* Give negative weight for doubled pawns. */
  1763.                         j += 10;
  1764.                         if (pieceColor == WHITE) j -= 20;
  1765.                         k = boardPtr[j];
  1766.                         if (k == OBNDS) break;
  1767.                         if (pieceColor == WHITE) k = -k;
  1768.                         if (k == PAWN) r -= 4;
  1769.                     }
  1770.                     if ((c == 3) || (c == 4)) {    /* Give negative weight for backward center pawns. */
  1771.                         for (j = square - 1;;) {
  1772.                             j += 10;
  1773.                             if (pieceColor == WHITE) j -= 20;
  1774.                             k = boardPtr[j];
  1775.                             if (k == OBNDS) break;
  1776.                             if (pieceColor == WHITE) k = -k;
  1777.                             if (k == PAWN) {
  1778.                                 r -= 2;
  1779.                                 break;
  1780.                             }
  1781.                         }
  1782.                         for (j = square + 1;;) {
  1783.                             j += 10;
  1784.                             if (pieceColor == WHITE) j -= 20;
  1785.                             k = boardPtr[j];
  1786.                             if (k == OBNDS) break;
  1787.                             if (pieceColor == WHITE) k = -k;
  1788.                             if (k == PAWN) {
  1789.                                 r -= 2;
  1790.                                 break;
  1791.                             }
  1792.                         }
  1793.                     }
  1794.                     for (j = square;;) {    /* Give negative weight for doubled pawns. */
  1795.                         j += 10;
  1796.                         if (pieceColor == WHITE) j -= 20;
  1797.                         k = boardPtr[j];
  1798.                         if (k == OBNDS) break;
  1799.                         if (pieceColor == WHITE) k = -k;
  1800.                         if (k == PAWN) r -= 4;
  1801.                     }
  1802.  
  1803.                     if ((*game)->doc.gameIndex == 2)
  1804.                         if (square == 54)
  1805.                             if ((boardPtr[63] == WP) || (boardPtr[65] == WP))
  1806.                                 r = -200;
  1807.                                     /* Special-case out center-counter. */
  1808.  
  1809.                     if ((*game)->doc.gameIndex == 4)
  1810.                         if (square == 45)
  1811.                             if (boardPtr[45] == BP)
  1812.                                 if (boardPtr[54] == BP)
  1813.                                     if (boardPtr[63] == WP)
  1814.                                         if (boardPtr[64] == WP)
  1815.                                             r += (0x00010000 >> 3);
  1816.                                                 /* Give weight to declining queen's gambit. */
  1817.  
  1818.                     if ((*game)->doc.gameIndex == 4)
  1819.                         if (square == 53)
  1820.                             if (boardPtr[53] == BP)
  1821.                                 if (boardPtr[43] == BN)
  1822.                                     if (boardPtr[65] == WP)
  1823.                                         if (boardPtr[76] == WKN)
  1824.                                             r += (0x00010000 >> 3);
  1825.                                                 /* Play a better e4,c5,Nf3. */
  1826.  
  1827.                     if ((*game)->doc.gameIndex == 5)
  1828.                         if (square == 54)
  1829.                             if (boardPtr[54] == WP)
  1830.                                 if (boardPtr[53] == BP)
  1831.                                     if (boardPtr[63] == WP)
  1832.                                         if (boardPtr[46] == BKN)
  1833.                                             r += (0x00010000 >> 2);
  1834.                                                 /* Play a better d4,Nf6,c4,c5. */
  1835.  
  1836.                     if ((*game)->doc.gameIndex == 6)
  1837.                         if (square == 64)
  1838.                             if (boardPtr[64] == BP)
  1839.                                 if (boardPtr[43] == BN)
  1840.                                     if (boardPtr[65] == WP)
  1841.                                         if (boardPtr[76] == WKN)
  1842.                                             r += (0x00010000 >> 2);
  1843.                                                 /* Play a better e4,c5,Nf3,Nc6,d4. */
  1844.  
  1845.                     if (color != pieceColor) r = -r;
  1846.                     posVal += r;
  1847.  
  1848.                 }
  1849.  
  1850.                 if (piece == KNIGHT) {        /* Give weight to centralized knights. */
  1851.                     if ((!r) || (r == 7)) {
  1852.                         r = -4;
  1853.                         if ((*game)->doc.gameIndex < 3) {
  1854.                             r = 200;
  1855.                             /* Special-case out knights early, 'cause it's really gross. */
  1856.                         }
  1857.                     }
  1858.                     else {
  1859.                         if (r == 4) r = 5;
  1860.                         if (r > 3) r = 7 - r;
  1861.                         if ((c < 2) || (c > 5))  r /= 2;
  1862.                         if ((c < 1) || (c > 6))  r /= 2;
  1863.                     }
  1864.                     if (color != pieceColor) r = -r;
  1865.                     posVal += r;
  1866.                 }
  1867.  
  1868.                 if (piece == BISHOP) {
  1869.                     j = r;
  1870.                     if (r > 5) r = 7 - r;
  1871.                         if (!r) r = -2;        /* Get those bishops developed. */
  1872.                     if ((c < 1) || (c > 6))  r /= 4;
  1873.                     if (j > 1) {        /* Give weight to knights before bishops past 2nd rank. */
  1874.                         c = (c < 4) ? 2 : 7;
  1875.                         k = boardPtr[90 + c - 70 * pieceColor];
  1876.                         if (pieceColor == WHITE) k = -k;
  1877.                         if (k < WK) k += KSIDEPIECE;
  1878.                         if (k > BK) k -= KSIDEPIECE;
  1879.                         if (k == KNIGHT) r = -1;
  1880.                     }
  1881.                     if (color != pieceColor) r = -r;
  1882.                     posVal += r;
  1883.                 }
  1884.  
  1885.                 if (piece == ROOK) {    /* Give weight to rooks on open files. */
  1886.                     if (material > 25) {
  1887.                         delta = (color == BLACK) ? 10 : -10;
  1888.                         if ((c) && (c < 7)) {
  1889.                             for (j = square + delta; !boardPtr[j]; j += delta, ++r) {};
  1890.                             if (color != pieceColor) r = -r;
  1891.                             posVal += r;
  1892.                         }
  1893.                     }
  1894.                 }
  1895.  
  1896.                 if (piece == QUEEN) {
  1897.                     if (color == pieceColor) {
  1898.                         if (movedPiece == QUEEN) {
  1899.                             if (material > 50) {
  1900.                                 r = -2;
  1901.                                 if (color != pieceColor) r = -r;
  1902.                                 posVal += r;
  1903.                             }        /* Keep the queen from moving too much in the beginning. */
  1904.                         }
  1905.                     }
  1906.                 }
  1907.                 if (piece == KING) {    /* Give weight to no king moves other than castling. */
  1908.                     j = 0;
  1909.                     if ((*game)->doc.king[color].kingMoves < 2) {
  1910.                         j = -10;
  1911.                         if (!r) {
  1912.                             if (c == 6) j = 6;
  1913.                             if (c == 2) j = 4;
  1914.                         }
  1915.                     }
  1916.                     if (color != pieceColor) j = -j;
  1917.                     posVal += j;
  1918.                 }
  1919.  
  1920.                 if (color == pieceColor) {
  1921.  
  1922.                     for (dirNum = 0; (dir = direction[piece][dirNum]) != 0; ++dirNum) {
  1923.                         for (s = square, dist = 1; dist <= distance[piece]; ++dist) {
  1924.                             if (piece == PAWN) {
  1925.                                 if (dirNum > 1) break;
  1926.                                 dir = direction[BISHOP][dirNum];
  1927.                                 if (color == BLACK) dir = -dir;
  1928.                             }
  1929.                             s += dir;
  1930.                             if ((take = (*game)->doc.theBoard[s]) == EMPTY) continue;
  1931.                             if (take == OBNDS) break;
  1932.                             xcolor = BLACK;
  1933.                             if (take < 0) {
  1934.                                 xcolor = WHITE;
  1935.                                 take = -take;
  1936.                             }
  1937.                             if (take > BK) take -= KSIDEPIECE;
  1938.                             if (xcolor == color) break;
  1939.                             switch (j = take) {
  1940.                                 case KNIGHT:
  1941.                                     j = 3;
  1942.                                     break;
  1943.                                 case ROOK:
  1944.                                     j = 5;
  1945.                                     break;
  1946.                                 case QUEEN:
  1947.                                     j = 9;
  1948.                                     break;
  1949.                                 case KING:
  1950.                                     j = material;
  1951.                                     if (j > 39) j = 60 - j;
  1952.                                     j /= 2;
  1953.                                     break;
  1954.                             }
  1955.                             switch (k = piece) {
  1956.                                 case KNIGHT:
  1957.                                     k = 3;
  1958.                                     break;
  1959.                                 case ROOK:
  1960.                                     k = 5;
  1961.                                     break;
  1962.                                 case QUEEN:
  1963.                                     k = 9;
  1964.                                     break;
  1965.                                 case KING:
  1966.                                     k = material;
  1967.                                     if (k > 39) k = 60 - k;
  1968.                                     k /= 2;
  1969.                                     break;
  1970.                             }
  1971.                             if (j > k) posVal += (j - k) * 5;
  1972.                             else if (!SquareAttacked(game, s, color)) posVal += (j + 4);
  1973.                             break;
  1974.                         }
  1975.                     }        /* The above code calculates the aggression factor of the board.
  1976.                             ** If the attacking piece is smaller than the piece attacked, then
  1977.                             ** we add the delta value as a plus.  This factor finds forks,
  1978.                             ** chases kings, queens, etc. */
  1979.  
  1980.                             /* The below code estimates the exchange value of takes.  This
  1981.                             ** is only an estimate, as it is not an actual move analysis
  1982.                             ** of all the possible exchanges.  We are only trying to get
  1983.                             ** an estimate of the board position in this function. */
  1984.  
  1985.                     takerLoc = SquareAttacked(game, square, color);
  1986.                     if (takerLoc) {
  1987.                         saveBoard = false;
  1988.                         xnum = val = 0;
  1989.                         for (xcolor = color ^ 1; takerLoc; xcolor ^= 1) {
  1990.                             take = boardPtr[square];
  1991.                             if (take < 0) take = -take;
  1992.                             if (take > BK) take -= KSIDEPIECE;
  1993.                             if (take == KING)   take = 512;
  1994.                             if (take == QUEEN)  take = 9;
  1995.                             if (take == ROOK)   take = 5;
  1996.                             if (take == KNIGHT) take = 3;
  1997.                             retakerLoc = SquareAttacked(game, square, xcolor);
  1998.                             if (xcolor == color) val += take;
  1999.                             else                 val -= take;
  2000.                             xcng[xnum++] = val;
  2001.                             if (retakerLoc) {
  2002.                                 if (!saveBoard) {
  2003.                                     saveBoard = true;
  2004.                                     for (j = START_IBNDS; j < END_IBNDS; ++j)
  2005.                                         keepBoard[j] = boardPtr[j];
  2006.                                 }
  2007.                                 boardPtr[square] = boardPtr[takerLoc];
  2008.                                 boardPtr[takerLoc] = 0;
  2009.                             }
  2010.                             takerLoc = retakerLoc;
  2011.                         }
  2012.                         if (saveBoard)
  2013.                             for (j = START_IBNDS; j < END_IBNDS; ++j)
  2014.                                 boardPtr[j] = keepBoard[j];
  2015.                         xcng[xnum] = xcng[xnum - 1];
  2016.                         xcolor = 1;        /* Check on opponent first. */
  2017.                         for (loop = true; loop; xcolor ^= 1) {
  2018.                             val  = xcng[xcolor];
  2019.                             k    = xnum;
  2020.                             loop = false;
  2021.                             for (j = xcolor + 2; j <= k; j += 2) {
  2022.                                 if (xcolor) {
  2023.                                     if (val > xcng[j]) {
  2024.                                         val = xcng[j];
  2025.                                         xnum = j - 1;
  2026.                                         loop = true;
  2027.                                     }
  2028.                                 }
  2029.                                 else {
  2030.                                     if (val < xcng[j]) {
  2031.                                         val = xcng[j];
  2032.                                         xnum = j - 1;
  2033.                                         loop = true;
  2034.                                     }
  2035.                                 }
  2036.                             }
  2037.                         }
  2038.                         if (xcngVal > val) xcngVal = val;
  2039.                     }
  2040.                 }
  2041.             }
  2042.         }
  2043.     }
  2044.  
  2045.     val = GameStatus(game);
  2046.     j = (100 - (*game)->doc.numLegalMoves) / 3;
  2047.     if (material > 60) j = 0;
  2048.  
  2049.     xcolor = color ^ 1;
  2050.     for (numChecks = 0;; ++numChecks) {
  2051.         if (!(SquareAttacked(game, (*game)->doc.king[xcolor].kingLoc, xcolor))) break;
  2052.         if ((*game)->doc.gameIndex < 2) break;
  2053.         UnmakeMove(game);
  2054.         UnmakeMove(game);
  2055.     }
  2056.     if (numChecks > 3) {
  2057.         j = 0;
  2058.     }
  2059.     for (; numChecks; --numChecks) {
  2060.         MakeMove(game, 1, 0, 0);
  2061.         MakeMove(game, 1, 0, 0);
  2062.     }
  2063.  
  2064.     posVal += j;
  2065.  
  2066.     if ((val >= kStalemate) && (val <= kDrawByRep)) matBalance = xcngVal = posVal = 0;
  2067.         /* Drawing may be our best move, so don't just eliminate this move. */
  2068.  
  2069.     if (gPosReps) matBalance = xcngVal = posVal = 0;
  2070.         /* Drawing may be our best move, so don't just eliminate this move. */
  2071.  
  2072.     UnmakeMove(game);
  2073.     GenerateLegalMoves(game);
  2074.  
  2075.     v1   = matBalance;
  2076.     v1  += xcngVal;
  2077.     v1 <<= 16;
  2078.     v2   = posVal;
  2079.     v2 <<= 3;
  2080.     v3   = Random() & 0x7F;
  2081.  
  2082.     value = v1 + v2;
  2083.     if (material > 29) value += v3;
  2084.  
  2085.     return(value);        
  2086. }
  2087.  
  2088.  
  2089.  
  2090. /*****************************************************************************/
  2091.  
  2092.  
  2093.  
  2094. void    SlideThePiece(FileRecHndl game, short fromSq, short toSq)
  2095. {
  2096.     short    fromRow, fromCol, toRow, toCol;
  2097.     Point    fromLoc, toLoc;
  2098.     Rect    fromRect;
  2099.  
  2100.     fromRow = (fromSq - START_IBNDS) / 10;
  2101.     fromCol = fromSq - START_IBNDS - 10 * fromRow;
  2102.     if ((*game)->doc.invertBoard) {
  2103.         fromRow = 7 - fromRow;
  2104.         fromCol = 7 - fromCol;
  2105.     }
  2106.     fromRect.top    = 1 + fromRow * kBoardSqSize;
  2107.     fromRect.left   = 1 + fromCol * kBoardSqSize;
  2108.     fromRect.bottom = fromRect.top  + 32;
  2109.     fromRect.right  = fromRect.left + 32;
  2110.  
  2111.     toRow = (toSq - START_IBNDS) / 10;
  2112.     toCol = toSq - START_IBNDS - 10 * toRow;
  2113.     if ((*game)->doc.invertBoard) {
  2114.         toRow = 7 - toRow;
  2115.         toCol = 7 - toCol;
  2116.     }
  2117.  
  2118.     fromLoc.v = fromRow * kBoardSqSize + kBoardVOffset + (kBoardSqSize / 2);
  2119.     fromLoc.h = fromCol * kBoardSqSize + kBoardHOffset + (kBoardSqSize / 2);
  2120.     toLoc.v   = toRow * kBoardSqSize + kBoardVOffset + (kBoardSqSize / 2);
  2121.     toLoc.h   = toCol * kBoardSqSize + kBoardHOffset + (kBoardSqSize / 2);
  2122.  
  2123.     MoveThePiece(game, fromSq, fromRect, fromLoc, &toLoc);
  2124. }
  2125.  
  2126.  
  2127.  
  2128. /*****************************************************************************/
  2129.  
  2130.  
  2131.  
  2132. void    CalcPositionValues(FileRecHndl game)
  2133. {
  2134.     short    *boardPtr, square, piece;
  2135.     long    val;
  2136.  
  2137.     boardPtr = &(*game)->doc.theBoard[0];
  2138.     gTreeValue = gWhiteTotal = gBlackTotal = gNumPieces = 0;
  2139.     for (square = START_IBNDS; square < END_IBNDS; ++square) {
  2140.         piece = boardPtr[square];
  2141.         if (piece) {
  2142.             if (piece != OBNDS) {
  2143.                 if (piece < WK) piece += KSIDEPIECE;
  2144.                 if (piece > BK) piece -= KSIDEPIECE;
  2145.                 val = gTreePieceValues[piece + KING];
  2146.                 gTreeValue += val;
  2147.                 if (piece < 0) piece = -piece;
  2148.                 if (piece != KING) {
  2149.                     gPieceLoc = square;
  2150.                     ++gNumPieces;
  2151.                     if (val < 0) gWhiteTotal -= val;
  2152.                     else         gBlackTotal += val;
  2153.                 }
  2154.             }
  2155.         }
  2156.     }
  2157. }
  2158.  
  2159.  
  2160.  
  2161.